package org.zjt.jsr;

import com.alibaba.fastjson.JSON;
import joptsimple.internal.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.http.util.Asserts;
import org.zjt.design.pattern.FlyweightTest;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.*;
import java.lang.reflect.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Description:
 * User: juntao.zhang
 * Date: 2018-05-17 10:33 AM
 *
 * @see
 */
@Slf4j
public class Lombok269 {



    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {



        String outputDir = "/Users/juntao.zhang/dev/workspace/myproject/bin/";
        String inputJava = "/Users/juntao.zhang/dev/workspace/myproject/bin/HotTest.java" ;

        Lombok269 lombok269 = new Lombok269();
        Optional<URL[]> urls = lombok269.fileDir2urls(outputDir);
        Asserts.check(urls.isPresent(),"文件不存在");
        log.info(JSON.toJSONString(urls));

        Asserts.check(lombok269.compilerJavaFile(inputJava,outputDir),"编译失败");
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

        Optional<URL[]> urls1 = lombok269.fileDir2urls(outputDir);
        Asserts.check(urls1.isPresent(),"文件不存在");
        MyHotURLClassLoader myHotURLClassLoader = new MyHotURLClassLoader(urls1.get(),systemClassLoader);

        Class<?> aClass = myHotURLClassLoader.loadClass("org.zjt.jsr.HotTest");
        Constructor<?> constructor = aClass.getConstructor(String.class, Integer.class);
        Object zhangsan = constructor.newInstance("zhangsan", 25);

        for(Parameter parameter: constructor.getParameters()){
            log.info(parameter.getName() +"   "+ parameter.isNamePresent());
        }

        Field age = aClass.getDeclaredField("age");
        log.info(aClass.toString());
        log.info(String.valueOf(age.get(zhangsan)));



        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            try {
                Class lombok269Class = Lombok269.class.getClass();
                Field classLoaderField = lombok269Class.getDeclaredField("classLoader");
                Field modifiersField = Field.class.getDeclaredField("modifiers");
                modifiersField.setAccessible(true);
                modifiersField.setInt(classLoaderField, classLoaderField.getModifiers() & ~Modifier.FINAL);
                classLoaderField.setAccessible(true);
                classLoaderField.set(Lombok269.class, myHotURLClassLoader);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return null;
        });

        ClassLoader classLoader = Lombok269.class.getClassLoader();
        log.info(classLoader.toString());

    }


    /**
     * 手动编译java文件
     * 功能：用来手动编译 .java的源文件，并在指定位置生成 .class的字节码文件
     * @param sourceFilePath：.java的源文件路径
     * @param classFilePath：.class的字节码文件路径
     * @return boolean：返回是否编译成功
     * @throws IOException
     * 如：compilerJavaFile("D:/sun/src/Demo.java", "D:/sun/bin")
     */
    public boolean compilerJavaFile(String sourceFilePath,
                                    String classFilePath) throws IOException {
        // 获取 JAVA编译器
        JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
        try (StandardJavaFileManager fileMgr = complier.getStandardFileManager(null, null, null)){

            // 获取 源文件
            Iterable units = fileMgr.getJavaFileObjects(sourceFilePath);
            // 将字节码文件放置到什么地方
            Iterable<String> options = Arrays.asList("-d", classFilePath);
            // 编译任务
            JavaCompiler.CompilationTask t = complier.getTask(null, fileMgr, null, options, null, units);
            // 进行编译
            Boolean bool = t.call();
            return bool;
        }
    }


    public Optional<URL[]> fileDir2urls(String outputDir){
        File file = new File(outputDir);
        if (!file.exists())
            return Optional.ofNullable(null);
        if (file.isFile()) {
            try {
                return Optional.ofNullable(new URL[]{file.toURI().toURL()});
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
        else if (file.isDirectory()){
            List<URL> collect = Arrays.stream(file.list()).map(item -> file.toString() + File.separator +item)
                    .map(this::fileDir2urls).filter(Optional::isPresent).map(Optional::get).flatMap(item -> Arrays.stream(item))
                    .collect(Collectors.toList());
            return Optional.ofNullable(collect.toArray(new URL[0]));
        }
        throw new UnsupportedOperationException(file.toString());
    }
}


class MyHotURLClassLoader extends URLClassLoader {

    public MyHotURLClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    @Override
    protected Class<?> findClass(final String name)
            throws ClassNotFoundException{
        // org.zjt.jsr.HotTest

        String className = name.replace(".", File.separator) + ".class";
        Optional<URL> any = Arrays.stream(this.getURLs()).filter(url -> url.toString().contains(className)).findAny();
        Asserts.check(any.isPresent(),name + ".class不存在");
        try {
            InputStream inputStream = any.get().openStream();
            byte[] bytes = IOUtils.toByteArray(inputStream);
            return defineClass(name, bytes, 0, bytes.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }

}
